/*
 * Copyright 2011 Kim Lindhardt Madsen
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package dk.lindhardt.gwt.geie.client.evaluate;

import org.matheclipse.parser.client.ast.*;
import org.matheclipse.parser.client.operator.*;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * User: AnAmuser
 * Date: 27-08-11
 * <p/>
 * TODO: Document this class
 */
public class GeieParserFactory implements IParserFactory {
   public final static int PLUS_PRECEDENCE = 2900;
   public final static int TIMES_PRECEDENCE = 3800;
   public final static int POWER_PRECEDENCE = 5700;

   static final String[] HEADER_STRINGS = {"Plus", "Less", "Divide", "LessEqual",
         "Times", "Power", "PreMinus", "GreaterEqual", "Colon", "Percent",
         "Equal", "StringJoin", "Unequal", "PrePlus", "Greater", "Subtract"};

   static final String[] OPERATOR_STRINGS = {"+", "<", "/", "<=",
         "*", "^", "-", ">=", ":", "%", "=", "&", "<>", "+", ">", "-"};

   static final Operator[] OPERATORS = {
         new InfixOperator("+", "Plus", PLUS_PRECEDENCE, InfixOperator.NONE),
         new InfixOperator("<", "Less", 2600, InfixOperator.NONE),
         new DivideOperator("/", "Divide", 4500, InfixOperator.LEFT_ASSOCIATIVE),
         new InfixOperator("<=", "LessEqual", 2600, InfixOperator.NONE),
         new InfixOperator("*", "Times", TIMES_PRECEDENCE, InfixOperator.NONE),
         new InfixOperator("^", "Power", POWER_PRECEDENCE, InfixOperator.RIGHT_ASSOCIATIVE),
         new PreMinusOperator("-", "PreMinus", 4600),
         new InfixOperator(">=", "GreaterEqual", 2600, InfixOperator.NONE),
         new InfixOperator(":", "Colon", 700, InfixOperator.NONE),
         new PostfixOperator("%", "Percent", 500),
         new InfixOperator("=", "Equal", 2600, InfixOperator.NONE),
         new InfixOperator("&", "StringJoin", 5800, InfixOperator.NONE),
         new InfixOperator("<>", "Unequal", 2600, InfixOperator.NONE),
         new PrePlusOperator("+", "PrePlus", 4600),
         new InfixOperator(">", "Greater", 2600, InfixOperator.NONE),
         new SubtractOperator("-", "Subtract", 2900, InfixOperator.LEFT_ASSOCIATIVE)};

   /**
    */
   private static HashMap<String, Operator> fOperatorMap;

   /**
    */
   private static HashMap<String, ArrayList<Operator>> fOperatorTokenStartSet;

   static {
      fOperatorMap = new HashMap<String, Operator>();
      fOperatorTokenStartSet = new HashMap<String, ArrayList<Operator>>();
      for (int i = 0; i < HEADER_STRINGS.length; i++) {
         addOperator(fOperatorMap, fOperatorTokenStartSet, OPERATOR_STRINGS[i], HEADER_STRINGS[i], OPERATORS[i]);
      }
   }

   /**
    * Create a default ASTNode factory
    *
    */
   public GeieParserFactory() {
   }

   static public void addOperator(
         final Map<String, Operator> operatorMap,
         final Map<String, ArrayList<Operator>> operatorTokenStartSet,
         final String operatorStr,
         final String headStr,
         final Operator oper) {
      ArrayList<Operator> list;
      operatorMap.put(headStr, oper);
      list = operatorTokenStartSet.get(operatorStr);
      if (list == null) {
         list = new ArrayList<Operator>(2);
         list.add(oper);
         operatorTokenStartSet.put(operatorStr, list);
      } else {
         list.add(oper);
      }
   }

   public String getOperatorCharacters() {
      return ":+-*/%^=><&";
   }

   /**
    * public Map<String, Operator> getIdentifier2OperatorMap()
    */
   public Map<String, Operator> getIdentifier2OperatorMap() {
      return fOperatorMap;
   }

   public Operator get(final String identifier) {
      return (Operator) fOperatorMap.get(identifier);
   }

   /**
    *
    */
   public Map<String, ArrayList<Operator>> getOperator2ListMap() {
      return fOperatorTokenStartSet;
   }

   /**
    *
    */
   public List<Operator> getOperatorList(final String key) {
      return fOperatorTokenStartSet.get(key);
   }

   static public InfixOperator createInfixOperator(
         final String operatorStr, final String headStr, final int precedence,
         final int grouping) {
      InfixOperator oper;
      if (headStr.equals("Divide")) {
         oper = new DivideOperator(operatorStr, headStr, precedence, grouping);
      } else if (headStr.equals("Subtract")) {
         oper = new SubtractOperator(operatorStr, headStr, precedence, grouping);
      } else {
         oper = new InfixOperator(operatorStr, headStr, precedence, grouping);
      }
      return oper;
   }

   static public PrefixOperator createPrefixOperator(
         final String operatorStr, final String headStr, final int precedence) {
      PrefixOperator oper;
      if (headStr.equals("PreMinus")) {
         oper = new PreMinusOperator(operatorStr, headStr, precedence);
      } else if (headStr.equals("PrePlus")) {
         oper = new PrePlusOperator(operatorStr, headStr, precedence);
      } else {
         oper = new PrefixOperator(operatorStr, headStr, precedence);
      }
      return oper;
   }

   static public PostfixOperator createPostfixOperator(
         final String operatorStr, final String headStr, final int precedence) {
      return new PostfixOperator(operatorStr, headStr, precedence);
   }

   public ASTNode createDouble(final String doubleString) {
      return new FloatNode(doubleString);
   }

   public FunctionNode createFunction(final SymbolNode head) {
      return new FunctionNode(head);
   }

   public FunctionNode createFunction(final SymbolNode head, final ASTNode arg0) {
      return new FunctionNode(head, arg0);
   }

   public FunctionNode createFunction(final SymbolNode head, final ASTNode arg0, final ASTNode arg1) {
      return new FunctionNode(head, arg0, arg1);
   }

   /**
    * Creates a new list with no arguments from the given header object .
    */
   public FunctionNode createAST(final ASTNode headExpr) {
      return new FunctionNode(headExpr);
   }

   public IntegerNode createInteger(final String integerString, final int numberFormat) {
      return new IntegerNode(integerString, numberFormat);
   }

   public IntegerNode createInteger(final int intValue) {
      return new IntegerNode(intValue);
   }

   public FractionNode createFraction(final IntegerNode numerator, final IntegerNode denominator) {
      return new FractionNode(numerator, denominator);
   }

   public PatternNode createPattern(final SymbolNode patternName, final ASTNode check) {
      return new PatternNode(patternName, check);
   }

   public StringNode createString(final StringBuffer buffer) {
      return new StringNode(buffer.toString());
   }

   public SymbolNode createSymbol(final String symbolName) {
      return new SymbolNode(symbolName);
   }

   public boolean isValidIdentifier(String identifier) {
      return true;
   }

}
